package org.monitor.http;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletResponse;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpOptions;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpTrace;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.monitor.kit.FileKit;

public class RestClient {
	
	private RestType type;
	private String url;
	private Map<String, String> pairs;
	private Map<String, File> files;
	
	private int socketOut = 8000;
	private int connOut = 10000;
	
	private CloseableHttpClient httpClient;
	
	public RestClient(String url){
		this(url, RestType.GET);
	}
	
	public RestClient(String url, RestType restType){
		this.url = url;
		this.type = restType;
		this.httpClient = HttpClientBuilder.create().build();
	}
	
	public RestResult execute(){
		RestResult rst = new RestResult();
		
		HttpRequestBase req = getRequest();
		try {
			req.setConfig(getReqConfig());
			HttpResponse resp = httpClient.execute(req);
			HttpEntity entity = resp.getEntity();
			if(HttpServletResponse.SC_OK != resp.getStatusLine().getStatusCode()){
				rst.setState(resp.getStatusLine().getStatusCode());
				rst.setMsg(EntityUtils.toString(entity));
				
			}else {
				rst.setData(EntityUtils.toString(entity));
			}
			
		} catch (Exception e) {
			rst.setState(-1);
			rst.setMsg(e.getMessage());
		}finally {
			FileKit.close(httpClient);
		}
		return rst;
	}
	
	protected RequestConfig getReqConfig() {
		RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketOut).setConnectTimeout(connOut).build();//设置请求和传输超时时间
		return requestConfig;
	}

	private HttpRequestBase getRequest(){
		HttpRequestBase req = null;
		switch(this.type){
			case POST:
				req = getPostReq();
				break;
			case DELETE:
				req = getDeleteReq();
				break;
			case HEAD:
				req = getHeadReq();
				break;
			case OPTIONS:
				req = getOptionsReq();
				break;
			case TRACE:
				req = getTraceReq();
				break;
			case PATCH:
				req = getPatchReq();
				break;
			case PUT:
				req = getPutReq();
				break;
			case GET:
			default:
				req = getGetReq();
				break;
		}
		return req;
	}
	
	protected HttpRequestBase getPostReq() {
		HttpRequestBase req = new HttpPost(url);
		
		List<NameValuePair> pairs = getPairs();
		if(files == null){
			if(pairs != null){
				try {
					((HttpPost) req).setEntity(new UrlEncodedFormEntity(pairs));
				} catch (UnsupportedEncodingException e) {
				}
			}
		}else {
			MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
			entityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
			if (pairs != null) {
				for (NameValuePair pair : pairs) {
					entityBuilder.addTextBody(pair.getName(), pair.getValue());
				}
			}

			for (Entry<String, File> entry : files.entrySet()) {
				entityBuilder.addBinaryBody(entry.getKey(), entry.getValue());
			}

			((HttpPost) req).setEntity(entityBuilder.build());
		}
		
		return req;
	}
	
	protected HttpRequestBase getGetReq(){
		HttpRequestBase req = new HttpGet(getUrlWithPairs());
		return req;
	}
	
	protected HttpRequestBase getPutReq(){
		HttpRequestBase req = new HttpPut(getUrlWithPairs());
		return req;
	}
	
	protected HttpRequestBase getPatchReq(){
		HttpRequestBase req = new HttpPatch(getUrlWithPairs());
		return req;
	}
	
	protected HttpRequestBase getTraceReq(){
		HttpRequestBase req = new HttpTrace(getUrlWithPairs());
		return req;
	}
	
	protected HttpRequestBase getHeadReq(){
		HttpRequestBase req = new HttpHead(getUrlWithPairs());
		return req;
	}
	
	protected HttpRequestBase getOptionsReq(){
		HttpRequestBase req = new HttpOptions(getUrlWithPairs());
		return req;
	}
	
	protected HttpRequestBase getDeleteReq(){
		HttpRequestBase req = new HttpDelete(getUrlWithPairs());
		return req;
	}
	
	private String getUrlWithPairs(){
		String urlWithParams = this.url;
		if(this.pairs != null && this.pairs.size() > 0){
			StringBuffer sb = new StringBuffer(this.url);
			if(sb.indexOf("?") > 0) {
				sb.append("&");
			}else {
				sb.append("?");
			}
			for (Entry<String, String> entry : pairs.entrySet()) {
				String name = entry.getKey();
				String value = entry.getValue();
				sb.append(name).append("=").append(value).append("&");
			}
			urlWithParams = sb.toString();
		}
		return urlWithParams;
	}

	private List<NameValuePair> getPairs() {
		List<NameValuePair> list = null;
		if(this.pairs != null && this.pairs.size() > 0){
			list = new ArrayList<NameValuePair>();
			for (Entry<String, String> entry : pairs.entrySet()) {
				String name = entry.getKey();
				String value = entry.getValue();
				NameValuePair pair = new BasicNameValuePair(name, value);
				list.add(pair);
			}
		}
		return list;
	}

	public void setPairs(Map<String, String> pairs) {
		this.pairs = pairs;
	}
	
	public void addPair(String name, String value){
		if(this.pairs == null){
			this.pairs = new HashMap<String, String>();
		}
		if(name != null && value != null){
			this.pairs.put(name, value);
		}
	}
	
	/**
	 * POST时才有效
	 * @param file
	 */
	public void addFile(String name, File file){
		if(this.files == null){
			this.files = new HashMap<String, File>();
		}
		this.files.put(name, file);
	}
	
	public void addFile(String name, String filePath){
		addFile(name, new File(filePath));
	}
	
	/**
	 * POST时才有效
	 * @param files
	 */
	public void setFiles(Map<String, File> files){
		this.files = files;
	}

	public void setSocketTimeout(int socketOut){
		this.socketOut = socketOut;
	}
	
	public void setConnectTimeout(int connOut){
		this.connOut = connOut;
	}
}
